<html>
<head>
<style>
body {
  font-family: sans-serif;
}
div#output {
  cursor: pointer;
}
div#switcher {
  cursor: pointer;
}
div#switcher a {
  border-top: 1px solid black;
  border-left: 1px solid black;
  padding-left: 0.5em;
  padding-right: 0.5em;
}
canvas.plot {
  border: 1px solid black;
}
div.plot-coordinates {
  font-family: monospace;
}
iframe {
  display: none;
  width: 100%;
  height: 100%;
  border: none;
}
div.selector {
  border: solid 1px black;
  cursor: pointer;
  padding-left: 0.3em;
  background-color: white;
}
div.selector:hover {
  background-color: rgb(200,200,250);
}
div.selected {
  border-left: none;
}
div#selectors {
  width: 80px;
  display: none;
}
.latest {
  font-weight: bold;
  color: rgb(60, 0, 240);
}
.reference {
  font-weight: bold;
  color: rgb(110, 50, 35);
}
</style>
<script src="js/common.js"></script>
<script src="js/coordinates.js"></script>
<script src="js/plotter.js"></script>
<script src="config.js"></script>
<script>
// TODO(pjohnson): Much of this code is common to all of the performance
// reports.  It would be nice to refactor the shared code into a common place.

document.title = Config.title;

String.prototype.startsWith = function(s) {
  return this.indexOf(s) == 0;
}

function strcmp(a, b) {
  return a < b ? -1 : (a > b ? 1 : 0);
}

// Hard-coded default trace to show if none are specified.
var defaultTrace = 'c:V8.OsMemoryAllocated';

var params = ParseParams();
if (!('history' in params)) {
  params.history = 150;
}
if (!('trace' in params)) {
  params.trace = defaultTrace;
}

function goTo(trace) {
  params.trace = trace;
  if (params.trace == '' && params.trace != '0') {
    params.trace = defaultTrace;
  }
  window.location.href = MakeURL(params);
}

function goToFromEvent(e) {
  var trace = e.target.value;
  return goTo(trace);
}

var didUpdatePositionDetails = false;

function unitsForTrace() {
  if ('trace' in params) {
    if (params.trace.startsWith("t:")) {
      return 'msec';
    }
  }
  return 'thing-a-ma-bobs';
}

function timing(dict) {
  return parseFloat(dict['time']);
}

function jsonToJs(data) {
  return eval('(' + data + ')')
}

function addSelectionTabs(rows) {
  if (rows.length > 0 && rows[0].length > 0) {
    data = jsonToJs(rows[0]);
    tabs = [];

    for (var clNumber in data) {
      for (var testName in data[clNumber]['latest']) {
        tabs.push(testName);
      }
    }

    tabs.sort(sortTraces);

    initPlotSwitcher(tabs);
  }
}

function appendTestResult(dataRows, currentData, testType, testName) {
  if (!dataRows[testType]) {
    dataRows[testType] = [];
  }

  mean = parseFloat(currentData[testName]['mean']);
  stdd = parseFloat(currentData[testName]['stdd']);
  dataRows[testType].push([mean, stdd]);
}

function onSummaryReceived(data) {
  var rows = data.split('\n');
  addSelectionTabs(rows);
  var clNumbers = [];
  var dataRows = {};

  for (var i = 0; i < rows.length; i++) {
    if (rows[i].length < 1) {
      break;
    }
    if (i > params.history) { // limit by history
      break;
    }

    clData = jsonToJs(rows[i]);

    for (var clNumber in clData) {
      clNumbers.push(clNumber);

      for (var testType in clData[clNumber]) {
        var currentData = clData[clNumber][testType];
        // Specific selection that is defined in params.trace.
        if (currentData[params.trace]) {
          appendTestResult(dataRows, currentData, testType, params.trace);
        }
      }
    }
  }
  
  // Don't depend on any special for-in order.
  var keys = [];
  for (var key in dataRows) {
    keys.push(key);
  }
  keys.sort();

  var dataGrid = [];
  for (var i = 0; i < keys.length; i++) {
    var key = keys[i];
    dataGrid.push(dataRows[key].reverse());
  }
  clNumbers.reverse();
  var plotter = new Plotter(clNumbers, dataGrid, Config.dataDescription,
    unitsForTrace(), document.getElementById("output"));
  plotter.onclick = handlePlotClicked;
  plotter.plot();

  return;
}

function handlePlotClicked(cl, value, fuzz, e) {
  document.getElementById('view-change').
    setAttribute('src', Config.changeLinkPrefix + cl);

  if (!didUpdatePositionDetails) {
    updatePositionDetails();
    didUpdatePositionDetails = true;
  }
}

function updatePositionDetails() {
  var output = document.getElementById("output");
  var win_height = window.innerHeight;
  var details = document.getElementById("views");
  var views = document.getElementById("views");
  var selectors = document.getElementById("selectors");
  selectors.style.display = "block";

  var views_width = output.offsetWidth - selectors.offsetWidth;

  views.style.border = "1px solid black";
  views.style.width = views_width + "px";
  views.style.height = (win_height - output.offsetHeight -
                        output.offsetTop - 30) + "px";

  selectors.style.position = "absolute";
  selectors.style.left = (views.offsetLeft + views_width + 1) + "px";
  selectors.style.top = views.offsetTop + "px";

  viewCl();
}

function viewCl(target) {
  document.getElementById("view-change").style.display = "block";
}

function addOption(selectBox, text, value) {
  var option = document.createElement("option");
  option.text = text;
  option.value = value;
  selectBox.add(option);
}

function initPlotSwitcher(tabs) {
  var selectBox = document.getElementById("switcher");

  if (selectBox.attachEvent) {
    selectBox.attachEvent("onchange", goToFromEvent);
  } else {
    selectBox.addEventListener("change", goToFromEvent, false);
  }

  var selectedIndex = 0;
  for (var i = 0; i < tabs.length; i++) {
    if (tabs[i] == params.trace) {
      selectedIndex = i;
    }
    addOption(selectBox, tabs[i], tabs[i]);
  }
  selectBox.selectedIndex = selectedIndex;

  if ('lookout' in params) {
    switcher.style.display = "none";
    details.style.display = "none";
    header_text.style.display = "none";
    explain.style.display = "none";
    selection.style.display = "none";
  } else {
    document.getElementById("header_lookout").style.display = "none";
  }
}

function log(data) {
  document.getElementById('log').appendChild(
       document.createTextNode(data + '\n'));
}

function init() {
  Fetch("summary.dat", onSummaryReceived);
}

// Used to sort by trace name, ignoring the "c:" and "t:" prefixes.
// This allows related traces (such as a timer and a counter for the same
// thing) to appear next to each other in the drop-down box.
function sortTraces(a, b) {
  function getMeat(trace) {
    if (trace.startsWith("c:") || trace.startsWith("t:")) {
      trace = trace.substring(2);
    }
    return trace;
  }

  var aMeat = getMeat(a);
  var bMeat = getMeat(b);
  var meatCmp = strcmp(aMeat, bMeat);
  
  if (meatCmp != 0) {
    return meatCmp;
  }
  return strcmp(a, b);
}

window.addEventListener("load", init, false);
</script>
</head>
<body>
<p>
<div id="header_lookout" align="center">
  <font style='color: #0066FF; font-family: Arial, serif;font-size: 20pt; font-weight: bold;'>
    <script>document.write(Config.title);</script>
  </font>
</div>
<div id="header_text">
Builds generated by the
<script>
document.write('<a href="' + Config.builderLink + '">' + Config.builder + '</a>');
</script>
build slave are run through the
<script>
document.write('<b>' + Config.title + '</b>');
</script>
and the results of that test are charted here.
</div>
</p>
<p style="font-size: 0.75em; font-style: italic; color: rgb(100,100,100)">
<div id="explain">
The vertical axis is the count or time and the horizontal axis is the
change-list for the build being tested.<br /><br />
<span class="latest">This color</span> is for the latest build, and
<span class="reference">this color</span> is for the reference build.
</div>
</p>
<select id="switcher">

</select>
<div id="output"></div>
<div id="details">
  <div id="views">
    <iframe id="view-change"></iframe>
    <iframe id="view-pages"></iframe>
  </div>
  <div id="selectors">
    <div class="selector" onclick="viewCl()">CL</div>
  </div>
</div>
<pre id="log"></pre>
</body>
</html>
